package com.example.online_gobang.api;

import com.example.online_gobang.game.*;
import com.example.online_gobang.mapper.UserMapper;
import com.example.online_gobang.model.User;
import com.example.online_gobang.tools.Constant;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.Random;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: 74646
 * Date: 2022-11-21
 * Time: 22:19
*/

@Component
public class GameAPI extends TextWebSocketHandler {

    @Autowired
    private OnlineUserManager onlineUserManager;

    @Autowired
    private RoomManager roomManager;

    @Autowired
    private ObjectMapper objectMapper;

    @Resource
    private UserMapper userMapper;

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        GameReadyResponse gameReadyResponse = new GameReadyResponse();

        // 1. 获取玩家信息
        User user = (User) session.getAttributes().get(Constant.USER_SESSION_KEY);
        if(user == null){
            gameReadyResponse.setStatus(-200);
            gameReadyResponse.setReason("用户未登录");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsBytes(gameReadyResponse)));
            return;
        }
        // 2. 判断当前玩家是否已经进入房间
        Room room = roomManager.getRoomByUserId(user.getUserId());
        if (room == null) {
            gameReadyResponse.setStatus(-200);
            gameReadyResponse.setMessage("玩家尚未匹配到!");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsBytes(gameReadyResponse)));
            return;
        }
        // 3. 判断当前玩家是否多开(如果一个账号一边在游戏大厅一边在游戏房间，这种也视为多开)
        if (onlineUserManager.getFromGameHall(user.getUserId()) != null || onlineUserManager.getFromGameRoom(user.getUserId()) != null) {
            gameReadyResponse.setReason("禁止玩家多开游戏页面!");
            gameReadyResponse.setStatus(-200);
            gameReadyResponse.setMessage("repeatConnection");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsBytes(gameReadyResponse)));
            return;
        }

        // 4. 设置当前玩家上线（进入游戏房间）
        onlineUserManager.enterGameRoom(user.getUserId(), session);

        // 5. 把两个玩家加入到游戏房间中
        //    前面的创建房间匹配过程, 是在 game_hall.html 页面中完成的.
        //    因此前面匹配到对手之后, 需要经过页面跳转, 来到 game_room.html 才算正式进入游戏房间(才算玩家准备就绪)
        //    当前这个逻辑是在 game_room.html 页面加载的时候进行的.
        //    执行到当前逻辑, 说明玩家已经页面跳转成功了!!
        synchronized (room) {
            if (room.getUser1() == null) {
                room.setUser1(user);
                System.out.println("玩家1 " + user.getUsername() + " 已经准备就绪");
                return;
            }

            if (room.getUser2() == null) {
                room.setUser2(user);
                System.out.println("玩家2 " + user.getUsername() + " 已经准备就绪");

                Random random = new Random();
                int num = random.nextInt(10);
                if (num % 2 == 0) {
                    room.setWhiteUser(room.getUser1().getUserId());
                } else{
                    room.setWhiteUser(room.getUser2().getUserId());
                }

                // 当两个玩家都加入成功之后, 就要让服务器, 给这两个玩家都返回 websocket 的响应数据.
                // 通知这两个玩家说, 游戏双方都已经准备好了.
                // 通知玩家1
                noticeGameReady(room,room.getUser1(),room.getUser2());
                // 通知玩家2
                noticeGameReady(room,room.getUser2(),room.getUser1());
                return;
            }
        }

        // 6. 如果又有其他玩家连接到已经满了的房间，给出一个提示。（这种情况理论上不存在）
        gameReadyResponse.setStatus(-200);
        gameReadyResponse.setMessage("当前房间已满");
        session.sendMessage(new TextMessage(objectMapper.writeValueAsBytes(gameReadyResponse)));
    }

    private void noticeGameReady(Room room, User thisUser, User thatUser) throws IOException {
        GameReadyResponse resp = new GameReadyResponse();
        resp.setStatus(200);
        resp.setReason("");
        resp.setMessage("gameReady");
        resp.setRoomId(room.getRoomId());
        resp.setThisUserId(thisUser.getUserId());
        resp.setThatUserId(thatUser.getUserId());
        resp.setWhiteUser(room.getWhiteUser());
        // 把当前的响应数据传回给玩家.
        WebSocketSession webSocketSession = onlineUserManager.getFromGameRoom(thisUser.getUserId());
        webSocketSession.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        // 1. 先从 session 中获取当前用户的身份信息
        User user = (User) session.getAttributes().get(Constant.USER_SESSION_KEY);
        if (user == null){
            System.out.println("[handleTextMessage]当前玩家"+ user.getUsername()+"未登录");
            return;
        }
        // 2. 根据玩家id 获取房间对象
        Room room =  roomManager.getRoomByUserId(user.getUserId());

        // 通过room对象处理这次请求
        room.putChess(message.getPayload());
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        // 玩家异常下线
        // 1. 获取玩家信息
        User user = (User) session.getAttributes().get(Constant.USER_SESSION_KEY);
        WebSocketSession webSocketSession = onlineUserManager.getFromGameRoom(user.getUserId());
        if(webSocketSession == session){
            // 2. 退出游戏房间
            onlineUserManager.exitGameRoom(user.getUserId());
        }
        System.out.println("当前用户: " + user.getUsername()+" 游戏房间连接异常！");

        // 通知对手获胜了
        noticeThatUserWin(user);
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        // 玩家正常下线
        // 1. 获取玩家信息
        User user = (User) session.getAttributes().get(Constant.USER_SESSION_KEY);
        WebSocketSession webSocketSession = onlineUserManager.getFromGameRoom(user.getUserId());
        if(webSocketSession == session){
            // 2. 退出游戏房间
            onlineUserManager.exitGameRoom(user.getUserId());
        }
        System.out.println("当前用户: " + user.getUsername()+" 离开房间");

        // 通知对手获胜了
        noticeThatUserWin(user);
    }

    private void noticeThatUserWin(User user) throws IOException {
        // 1. 根据当前玩家, 找到玩家所在的房间
        Room room = roomManager.getRoomByUserId(user.getUserId());
        if (room == null) {
            // 这个情况意味着房间已经被释放了, 也就没有 "对手" 了
            System.out.println("当前房间已关闭, 无需通知对手!");
            return;
        }

        // 2. 根据房间找到对手
        User thatUser = (user == room.getUser1()) ? room.getUser2() : room.getUser1();
        // 3. 获取对手的在线状态
        WebSocketSession webSocketSession = onlineUserManager.getFromGameRoom(thatUser.getUserId());
        if (webSocketSession == null) {
            // 对手掉线了!
            System.out.println("对方掉线了, 无需通知!");
            return;
        }
        // 4. 构造一个响应, 来通知对手, 你是获胜方
        GameResponse resp = new GameResponse();
        resp.setMessage("putChess");
        resp.setUserId(thatUser.getUserId());
        resp.setWinner(thatUser.getUserId());
        webSocketSession.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));

        // 5. 更新玩家的分数信息
        int winUserId = thatUser.getUserId();
        int loseUserId = user.getUserId();
        userMapper.userWin(winUserId);
        userMapper.userLose(loseUserId);

        // 6. 释放房间对象
        roomManager.remove(room.getRoomId(), room.getUser1().getUserId(), room.getUser2().getUserId());
    }
}
